home *** CD-ROM | disk | FTP | other *** search
Text File | 1987-05-17 | 26.0 KB | 802 lines | [TEXT/QED1] |
- ; Low Level MIDI routines with time-stamping
- ; Written by Kirk Austin 5/17/87
- ; This code is in the public domain and is absolutely free
-
- ; Serial Chip equates
- SCCRd EQU $1D8
- SCCWr EQU $1DC
- aData EQU 6
- aCtl EQU 2
- bData EQU 4
- bCtl EQU 0
- TBE EQU 2
-
- ; Interrupt vector equates
- Lvl1DT EQU $192
- Lvl2DT EQU $1B2
- RxIntOffsetA EQU 24
- TxIntOffsetA EQU 16
- SpecRecCondA EQU 28
- RxIntOffsetB EQU 8
- TxIntOffsetB EQU 0
- SpecRecCondB EQU 12
-
- ; 6522 equates
- VIA EQU $1D4
- vT1C EQU $800
- vT1CH EQU $A00
- vT1L EQU $C00
- vACR EQU $1600
- vIER EQU $1C00
-
- ; XDEF all routines that need to be accessed externally.
-
- XDEF InitSCCA
- XDEF InitSCCB
- XDEF TxMIDIA
- XDEF TxMIDIB
- XDEF RxMIDIA
- XDEF RxMIDIB
- XDEF ResetSCCA
- XDEF ResetSCCB
- XDEF InitTimer
- XDEF LoadTimer
- XDEF StartCounter
- XDEF GetCounter
- XDEF QuitTimer
-
- ; These are the routines for the Modem Port
-
-
- ; PROCEDURE InitSCCA;
- ; Call this routine at the beginning of your application if you are going
- ; to be using the modem port for MIDI information transfers.
- InitSCCA
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A2,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCRd,A1 ; Get base Read address
- ADD #aCtl,A1 ; Add offset for control
- MOVE.B (A1),D0 ; Dummy read
- MOVE.L (SP),(SP) ; Delay
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #aCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%10000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR InitSCCChan ; branch to common init routine
-
- ; set up the interrupt vectors
-
- MOVE.L #Lvl2DT,A0 ; get dispatch table pointer
- MOVE #RxIntOffsetA,D0 ; get offset to Rx vector
- LEA PRxIntHandA,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),(A1) ; save previous int vector
- LEA RxIntHandA,A1 ; set Rx vector
- MOVE.L A1,0(A0,D0)
- MOVE #TxIntOffsetA,D0 ; get offset to Tx vector
- LEA PTxIntHandA,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),(A1) ; save previous int vector
- LEA TxIntHandA,A1 ; set Tx vector
- MOVE.L A1,0(A0,D0)
- MOVE #SpecRecCondA,D0 ; get offset to Special vector
- LEA StubA,A1
- MOVE.L A1,0(A0,D0)
-
- ; initialize the flags & pointers
-
- LEA RxByteInA,A2 ; get the address
- CLR (A2)
- LEA RxByteOutA,A2 ; get the address
- CLR (A2)
- LEA RxQEmptyA,A2 ; get the address
- MOVE #$FFFF,(A2)
- LEA TxByteInA,A2 ; get the address
- CLR (A2)
- LEA TxByteOutA,A2 ; get the address
- CLR (A2)
- LEA TxQEmptyA,A2 ; get the address
- MOVE #$FFFF,(A2)
-
- MOVEM.L (SP)+,D0/A0-A2 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- RTS ; and return
-
- ; This is the common initialzation routine for both channels
-
- InitSCCChan
- MOVE.B #4,(A0) ; pointer for SCC reg 4
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%10000100,(A0) ; 32x clock, 1 stop bit
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #1,(A0) ; pointer for SCC reg 1
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; No W/Req
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #3,(A0) ; pointer for SCC reg 3
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; Turn off Rx
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #5,(A0) ; pointer for SCC reg 5
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; Turn off Tx
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #11,(A0) ; pointer for SCC reg 11
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00101000,(A0) ; Make TRxC clock source
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #14,(A0) ; pointer for SCC reg 14
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000000,(A0) ; Disable BRGen
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #3,(A0) ; pointer for SCC reg 3
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%11000001,(A0) ; Enable Rx
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #5,(A0) ; pointer for SCC reg 5
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%01101010,(A0) ; Enable Tx and drivers
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #15,(A0) ; pointer for SCC reg 15
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001000,(A0) ; Enable DCD int for mouse
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS again
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #1,(A0) ; pointer for SCC reg 1
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010011,(A0) ; Enable interrupts
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001010,(A0) ; Set master int enable
- MOVE.L (SP),(SP) ; Delay
- RTS
-
- ; PROCEDURE TxMIDIA (TheData : integer);
- ; This is the routine to transmit a MIDI byte of data through the Modem Port.
- ; To use this routine place the byte to be transmitted as the lower 8 bits
- ; of a word on the stack, then call TxMIDIA.
- TxMIDIA
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- LEA TxQEmptyA,A3 ; get the address
- TST.B (A3) ; is TxQueue empty?
- BNE TxQEA ; if so branch
- LEA TxByteInA,A3 ; get the address
- MOVE (A3),D0 ; if not add byte to queue
- LEA TxQueueA,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update TxByteIn
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- BRA TxExitA ; and exit
-
- TxQEA MOVE.L SCCRd,A0 ; get SCC Read Address
- MOVE.L SCCWr,A1 ; get SCC Write address
- MOVE #aCtl,D0 ; get index for Ctl
- BTST.B #TBE,0(A0,D0) ; transmit buffer empty?
- BNE FirstByteA ; if so branch
- LEA TxByteInA,A3 ; get the address
- MOVE (A3),D0 ; if not add to queue
- LEA TxQueueA,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update pointer
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- LEA TxQEmptyA,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- BRA TxExitA ; and exit
-
- FirstByteA
- MOVE #aData,D0 ; get index to data
- MOVE.L (SP),(SP) ; delay
- MOVE.B 9(A6),0(A1,D0) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
-
-
- TxExitA MOVEM.L (SP)+,D0/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- UNLK A6 ; release frame pointer
- MOVE.L (SP)+,A1 ; save return address
- ADD.L #2,SP ; move past data word
- MOVE.L A1,-(SP) ; put address back on stack
- RTS ; and return
-
- ; FUNCTION RxMIDIA : LongInt;
- ; This routine gets a byte through the modem port. To use this routine
- ; treat it like a Pascal function. Leave space on the stack for a longword
- ; of data before calling this routine. If the data on the stack after
- ; the routine executes is 0 there was no MIDI data available. If it's non-0
- ; the upper 3 bytes contain the counter value, the MIDI byte is the low byte.
- RxMIDIA
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0-D1/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; disable interrupts
-
- LEA RxQEmptyA,A3 ; get the address
- TST.B (A3) ; any data available?
- BEQ @1 ; if so, branch
- MOVE.L #0,8(A6) ; if not, return with 0
- BRA RxExitA
- @1 LEA RxByteOutA,A3 ; get the address
- MOVE (A3),D0 ; get index to byte out
- LEA RxQueueA,A2 ; point to queue
- MOVE.L #0,D1 ; clear data register
- MOVE.L 0(A2,D0),D1 ; get MIDI data
- MOVE.L D1,8(A6) ; place it on stack for return
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @2
- MOVE #0,D0
- @2 LEA RxByteOutA,A3 ; get the address
- MOVE D0,(A3)
- LEA RxByteInA,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is queue empty?
- BNE RxExitA ; if not exit
- LEA RxQEmptyA,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty, set flag
-
- RxExitA MOVEM.L (SP)+,D0-D1/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; restore interrupts
- UNLK A6
- RTS ; and return
-
- ; This is the interrupt routine for receiving through the modem port.
- ; It places the counter value and the MIDI byte in a circular queue to be
- ; accessed later by the application.
- ; When the system gets this far, A0 contains the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may be used freely.
- RxIntHandA
- ORI #$0300,SR ; disable interrupts
-
- @3 MOVE #4,D0 ; get data offset
- CLR.L D1 ; prepare for data
- MOVE.L (SP),(SP) ; Delay
- MOVE.B 0(A0,D0),D1 ; read data from SCC
- MOVE.L (SP),(SP) ; Delay
- LEA RxQueueA,A2 ; point to queue
- LEA RxByteInA,A3 ; get the address
- MOVE (A3),D0 ; get offset to next cell
- LEA Counter,A3 ; get the address
- MOVE.L (A3),D2 ; put counter value in D2
- LSL.L #8,D2 ; shift counter one byte
- ADD.L D2,D1 ; combine counter and data
- MOVE.L D1,0(A2,D0) ; put longword in queue
- LEA RxQEmptyA,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @1
- MOVE #0,D0
- @1 LEA RxByteInA,A3 ; get the address
- MOVE D0,(A3)
-
- @2 BTST.B #0,(A0) ; is there more data?
- BNE @3 ; do it again if there is
-
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; This is the interrupt routine for transmitting a byte through the modem port.
- ; It checks to see if there is any data to send, and if there is it sends it to
- ; the SCC. If there isn't it resets the TBE interrupt in the SCC and exits.
- ; When the system gets this far, A0 contains the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may be used freely.
- TxIntHandA
- ORI #$0300,SR ; disable interrupts
-
- LEA TxQEmptyA,A3 ; get the address
- TST.B (A3) ; Is queue empty?
- BEQ @1 ; if not branch
- MOVE.B #$28,(A1) ; if so, reset TBE interrupt
- MOVE.L (SP),(SP) ; Delay
- BRA TxIExitA ; and exit
- @1 LEA TxByteOutA,A3 ; get the address
- MOVE (A3),D0 ; get index to next data byte
- LEA TxQueueA,A2 ; point to queue
- MOVE #4,D1 ; get data offset
- MOVE.B 0(A2,D0),0(A1,D1) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
- ADDQ #1,D0 ; update index
- CMP #$100,D0
- BNE @2
- MOVE #0,D0
- @2 LEA TxByteOutA,A3 ; get the address
- MOVE D0,(A3)
- LEA TxByteInA,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is TxQueue empty?
- BNE TxIExitA ; if not exit
- LEA TxQEmptyA,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty set flag
-
- TxIExitA
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
-
- ; PROCEDURE ResetSCCA;
- ; If you called InitSCCA at the beginning of your application this
- ; routine must be called when the application quits or the system will
- ; crash due to the interrupt handling pointers becoming invalid.
- ResetSCCA
- MOVEM.L D0/A0-A1,-(SP) ; save registers
- MOVE SR,-(SP) ; Save interrupts
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #aCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%10000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR ResetSCCChan ; branch to the common reset routine
-
- MOVE.L #Lvl2DT,A0 ; get dispatch table pointer
- MOVE #RxIntOffsetA,D0 ; get offset to Rx vector
- LEA PRxIntHandA,A1 ; point to previous vector storage
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
- MOVE #TxIntOffsetA,D0 ; get offset to Tx vector
- LEA PTxIntHandA,A1 ; set Rx vector
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
-
- MOVE (SP)+,SR ; Restore interrupts
- MOVEM.L (SP)+,D0/A0-A1 ; restore registers
- RTS ; and return
-
- ; This is the common reset routine for both channels
-
- ResetSCCChan
- MOVE.B #15,(A0) ; pointer for SCC reg 15
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001000,(A0) ; Enable DCD int
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #0,(A0) ; pointer for SCC reg 0
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00010000,(A0) ; Reset EXT/STATUS again
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #1,(A0) ; pointer for SCC reg 1
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00000001,(A0) ; Enable mouse interrupts
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%00001010,(A0) ; Set master int enable
- MOVE.L (SP),(SP) ; Delay
- RTS
-
- TxQueueA DCB.B $100,0 ; this is the queue
- TxQEmptyA DC 0 ; the queue empty flag
- TxByteInA DC 0 ; index to next cell in
- TxByteOutA DC 0 ; index to next cell out
- RxQueueA DCB.B $400,0 ; this is the queue
- RxQEmptyA DC 0 ; the queue empty flag
- RxByteInA DC 0 ; index to next cell in
- RxByteOutA DC 0 ; index to next cell out
- PRxIntHandA DC.L 0 ; Previous interrupt vector
- PTxIntHandA DC.L 0 ; Previous interrupt vector
-
- ; These are the routines for the Printer Port
-
- ; PROCEDURE InitSCCB;
- ; Call this routine at the beginning of your application if you are going
- ; to be using the printer port for MIDI information transfers.
- InitSCCB
-
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A2,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCRd,A1 ; Get base Read address
- ADD #bCtl,A1 ; Add offset for control
- MOVE.B (A1),D0 ; Dummy read
- MOVE.L (SP),(SP) ; Delay
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #bCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%01000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR InitSCCChan ; branch to common init routine
-
- ; set up the interrupt vectors
-
- MOVE.L #Lvl2DT,A0 ; get dispatch table pointer
- MOVE #RxIntOffsetB,D0 ; get offset to Rx vector
- LEA PRxIntHandB,A1 ; point to previous vector storage
- MOVE.L 0(A0,D0),(A1) ; save previous int vector
- LEA RxIntHandB,A1 ; set Rx vector
- MOVE.L A1,0(A0,D0)
- MOVE #TxIntOffsetB,D0 ; get offset to Tx vector
- LEA PTxIntHandB,A1 ; set Rx vector
- MOVE.L 0(A0,D0),(A1) ; save previous int vector
- LEA TxIntHandB,A1 ; set Tx vector
- MOVE.L A1,0(A0,D0)
- MOVE #SpecRecCondB,D0 ; get offset to Special vector
- LEA StubB,A1
- MOVE.L A1,0(A0,D0)
-
- ; initialize the flags & pointers
-
- LEA RxByteInB,A2 ; get the address
- CLR (A2)
- LEA RxByteOutB,A2 ; get the address
- CLR (A2)
- LEA RxQEmptyB,A2 ; get the address
- MOVE #$FFFF,(A2)
- LEA TxByteInB,A2 ; get the address
- CLR (A2)
- LEA TxByteOutB,A2 ; get the address
- CLR (A2)
- LEA TxQEmptyB,A2 ; get the address
- MOVE #$FFFF,(A2)
-
- MOVEM.L (SP)+,D0/A0-A2 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- RTS ; and return
-
- ; PROCEDURE TxMIDIB (TheData : integer);
- ; This is the routine to transmit a MIDI byte of data through the Printer Port.
- ; To use this routine place the byte to be transmitted as the lower 8 bits
- ; of a word on the stack, then call TxMIDIB.
- TxMIDIB
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; Disable interrupts
-
- LEA TxQEmptyB,A3 ; get the address
- TST.B (A3) ; is TxQueue empty?
- BNE TxQEB ; if so branch
- LEA TxByteInB,A3 ; get the address
- MOVE (A3),D0 ; if not add byte to queue
- LEA TxQueueB,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update TxByteIn
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- BRA TxExitB ; and exit
-
- TxQEB MOVE.L SCCRd,A0 ; get SCC Read Address
- MOVE.L SCCWr,A1 ; get SCC Write address
- MOVE #bCtl,D0 ; get index for Ctl
- BTST.B #TBE,0(A0,D0) ; transmit buffer empty?
- BNE FirstByteB ; if so branch
- LEA TxByteInB,A3 ; get the address
- MOVE (A3),D0 ; if not add to queue
- LEA TxQueueB,A2 ; point to queue
- MOVE.B 9(A6),0(A2,D0) ; place byte in queue
- ADDQ #1,D0 ; update pointer
- CMP #$100,D0
- BNE @1
- MOVE #0,D0
- @1 MOVE D0,(A3)
- LEA TxQEmptyB,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- BRA TxExitB ; and exit
-
- FirstByteB
- MOVE #bData,D0 ; get index to data
- MOVE.L (SP),(SP) ; delay
- MOVE.B 9(A6),0(A1,D0) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
-
-
- TxExitB MOVEM.L (SP)+,D0/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; Restore interrupts
- UNLK A6 ; release frame pointer
- MOVE.L (SP)+,A1 ; save return address
- ADD.L #2,SP ; move past data word
- MOVE.L A1,-(SP) ; put address back on stack
- RTS ; and return
-
- ; FUNCTION RxMIDIB : LongInt;
- ; This routine gets a byte through the printer port. To use this routine
- ; treat it like a Pascal function. Leave space on the stack for a longword
- ; of data before calling this routine. If the data on the stack after
- ; the routine executes is 0 there was no MIDI data available. If it's non-0
- ; the upper 3 bytes contain the counter value, the MIDI byte is the low byte.
- RxMIDIB
- LINK A6,#0 ; set frame pointer
- MOVE SR,-(SP) ; Save interrupts
- MOVEM.L D0-D1/A0-A3,-(SP) ; Save registers
- ORI #$0300,SR ; disable interrupts
-
- LEA RxQEmptyB,A3 ; get the address
- TST.B (A3) ; any data available?
- BEQ @1 ; if so, branch
- MOVE.L #0,8(A6) ; if not, return with 0
- BRA RxExitB
- @1 LEA RxByteOutB,A3 ; get the address
- MOVE (A3),D0 ; get index to byte out
- LEA RxQueueB,A2 ; point to queue
- MOVE.L #0,D1 ; clear data register
- MOVE.L 0(A2,D0),D1 ; get MIDI data
- MOVE.L D1,8(A6) ; place it on stack for return
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @2
- MOVE #0,D0
- @2 LEA RxByteOutB,A3 ; get the address
- MOVE D0,(A3)
- LEA RxByteInB,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is queue empty?
- BNE RxExitB ; if not exit
- LEA RxQEmptyB,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty, set flag
-
- RxExitB MOVEM.L (SP)+,D0-D1/A0-A3 ; Restore registers
- MOVE (SP)+,SR ; restore interrupts
- UNLK A6
- RTS ; and return
-
- ; This is the interrupt routine for receiving through the printer port.
- ; It places the counter value and the MIDI byte in a circular queue to be
- ; accessed later by the application.
- ; When the system gets this far, A0 contains the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may be used freely.
- RxIntHandB
- ORI #$0300,SR ; disable interrupts
-
- @3 MOVE #4,D0 ; get data offset
- CLR.L D1 ; prepare for data
- MOVE.L (SP),(SP) ; Delay
- MOVE.B 0(A0,D0),D1 ; read data from SCC
- MOVE.L (SP),(SP) ; Delay
- LEA RxQueueB,A2 ; point to queue
- LEA RxByteInB,A3 ; get the address
- MOVE (A3),D0 ; get offset to next cell
- LEA Counter,A3 ; get the address
- MOVE.L (A3),D2 ; put counter value in D2
- LSL.L #8,D2 ; shift counter one byte
- ADD.L D2,D1 ; combine counter and data
- MOVE.L D1,0(A2,D0) ; put longword in queue
- LEA RxQEmptyB,A3 ; get the address
- MOVE #0,(A3) ; reset queue empty flag
- ADDQ #4,D0 ; update index
- CMP #$400,D0
- BNE @1
- MOVE #0,D0
- @1 LEA RxByteInB,A3 ; get the address
- MOVE D0,(A3)
-
- @2 BTST.B #0,(A0) ; is there more data?
- BNE @3 ; do it again if there is
-
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; This is the interrupt routine for transmitting a byte through the printer port.
- ; It checks to see if there is any data to send, and if there is it sends it to
- ; the SCC. If there isn't it resets the TBE interrupt in the SCC and exits.
- ; When the system gets this far, A0 contains the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may be used freely.
- TxIntHandB
- ORI #$0300,SR ; disable interrupts
-
- LEA TxQEmptyB,A3 ; get the address
- TST.B (A3) ; Is queue empty?
- BEQ @1 ; if not branch
- MOVE.B #$28,(A1) ; if so, reset TBE interrupt
- MOVE.L (SP),(SP) ; Delay
- BRA TxIExitB ; and exit
- @1 LEA TxByteOutB,A3 ; get the address
- MOVE (A3),D0 ; get index to next data byte
- LEA TxQueueB,A2 ; point to queue
- MOVE #4,D1 ; get data offset
- MOVE.B 0(A2,D0),0(A1,D1) ; write data to SCC
- MOVE.L (SP),(SP) ; Delay
- ADDQ #1,D0 ; update index
- CMP #$100,D0
- BNE @2
- MOVE #0,D0
- @2 LEA TxByteOutB,A3 ; get the address
- MOVE D0,(A3)
- LEA TxByteInB,A3 ; get the address
- MOVE (A3),D1
- CMP D0,D1 ; is TxQueue empty?
- BNE TxIExitB ; if not exit
- LEA TxQEmptyB,A3 ; get the address
- MOVE #$FFFF,(A3) ; if empty set flag
-
- TxIExitB
- ANDI #$F8FF,SR ; enable interrupts
- RTS ; and return
-
- ; PROCEDURE ResetSCCB;
- ; If you called InitSCCB at the beginning of your application this
- ; routine must be called when the application quits or the system will
- ; crash due to the interrupt handling pointers becoming invalid.
- ResetSCCB
- MOVEM.L D0/A0-A1,-(SP) ; save registers
- MOVE SR,-(SP) ; Save interrupts
- ORI #$0300,SR ; Disable interrupts
-
- MOVE.L SCCWr,A0 ; Get base Write address
- ADD #bCtl,A0 ; Add offset for control
- MOVE.B #9,(A0) ; pointer for SCC reg 9
- MOVE.L (SP),(SP) ; Delay
- MOVE.B #%01000000,(A0) ; Reset channel
- MOVE.L (SP),(SP) ; Delay
- BSR ResetSCCChan ; branch to the common reset routine
-
- MOVE.L #Lvl2DT,A0 ; get dispatch table pointer
- MOVE #RxIntOffsetB,D0 ; get offset to Rx vector
- LEA PRxIntHandB,A1 ; point to previous vector storage
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
- MOVE #TxIntOffsetB,D0 ; get offset to Tx vector
- LEA PTxIntHandB,A1 ; set Rx vector
- MOVE.L (A1),0(A0,D0) ; restore previous int vector
-
- MOVE (SP)+,SR ; Restore interrupts
- MOVEM.L (SP)+,D0/A0-A1 ; restore registers
- RTS ; and return
-
- TxQueueB DCB.B $100,0 ; this is the queue
- TxQEmptyB DC 0 ; the queue empty flag
- TxByteInB DC 0 ; index to next cell in
- TxByteOutB DC 0 ; index to next cell out
- RxQueueB DCB.B $400,0 ; this is the queue
- RxQEmptyB DC 0 ; the queue empty flag
- RxByteInB DC 0 ; index to next cell in
- RxByteOutB DC 0 ; index to next cell out
- PRxIntHandB DC.L 0 ; Previous interrupt vector
- PTxIntHandB DC.L 0 ; Previous interrupt vector
-
- ; This is the space for a special condition interrupt routine.
- ; All I do here is reset the error flag in the SCC and return.
- ; When the system gets this far, A0 contains the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may be used freely.
- StubA
- ORI #$0300,SR ; Disable interrupts
- MOVE.B #%00110000,(A1) ; Reset Error
- MOVE.L (SP),(SP) ; Delay
- ANDI #$F8FF,SR ; Restore interrupts
-
- RTS
-
- ; This is the space for a special condition interrupt routine.
- ; All I do here is reset the error flag in the SCC and return.
- ; When the system gets this far, A0 contains the SCC base read Ctl address
- ; and A1 contains the SCC base write Ctl address for this channel.
- ; The data addresses are offset by 4 from the control addresses.
- ; D0-D3/A0-A3 are already preserved, so they may be used freely.
- StubB
- ORI #$0300,SR ; Disable interrupts
- MOVE.B #%00110000,(A1) ; Reset Error
- MOVE.L (SP),(SP) ; Delay
- ANDI #$F8FF,SR ; Restore interrupts
-
- RTS
-
- ; These are the routines for the counter you can use for time-stamping the
- ; incoming MIDI data. This is useful for writing sequencer type applications.
- ; The time-stamping is done on an interrupt level, is extremely accurate,
- ; and uses the VIA timer #1. This means that you can't use any of the
- ; Sound Manager routines because they use timer #1 too. If you want to
- ; create a metronome click you have to write your own code that accesses
- ; the sound hardware directly without using timer #1.
- ; InitTimer and LoadTimer expect a word on the stack to load the timer.
- ; To increment the counter every millisecond, load the timer with decimal 782
- ; If you aren't going to use time-stamping you can ignore these routines.
-
- ; PROCEDURE InitTimer (TimrValue : integer);
- ; Only call InitTimer once at the beginning of your application
- ; 1 millisecond is decimal 782.
- InitTimer
- LINK A6,#0 ; set frame pointer
- MOVEM.L D0/A0-A1,-(SP)
- MOVE.L #Lvl1DT,A0 ; Point to level 1 dispatch table
- LEA PrevIVC,A1 ; point to interrupt vector storage
- MOVE.L 24(A0),(A1) ; save the previous interrupt vector
- LEA CounterIntHand,A1 ; point to the new interrupt handler
- MOVE.L A1,24(A0) ; put it in the dispatch table
- MOVE.L VIA,A1 ; point to the 6522 chip
- ORI.B #$40,vACR(A1) ; set the timer to freerun mode
- MOVE.B #$C0,vIER(A1) ; Enable timer interrupts
- MOVE 8(A6),D0 ; Get timer value
- MOVE.B D0,vT1L(A1) ; set timer lo byte
- LSR #8,D0 ; shift to hi byte
- MOVE.B D0,vT1CH(A1) ; set timer hi byte
- MOVEM.L (SP)+,D0/A0-A1
- UNLK A6
- MOVE.L (SP)+,A0 ; save return address
- ADDQ #2,SP ; move past timer value
- MOVE.L A0,-(SP) ; replace return address
- RTS
-
- ; PROCEDURE LoadTimer (TimrValue : integer);
- ; Call LoadTimer whenever you want to change the timer value.
- ; 1 millisecond is decimal 782.
- LoadTimer
- LINK A6,#0 ; set frame pointer
- MOVEM.L D0/A0-A1,-(SP)
- MOVE.L VIA,A1 ; point to the 6522 chip
- MOVE 8(A6),D0 ; Get timer value
- MOVE.B D0,vT1L(A1) ; set timer lo byte
- LSR #8,D0 ; shift to hi byte
- MOVE.B D0,vT1CH(A1) ; set timer hi byte
- MOVEM.L (SP)+,D0/A0-A1
- UNLK A6
- MOVE.L (SP)+,A0 ; save return address
- ADDQ #2,SP ; move past timer value
- MOVE.L A0,-(SP) ; replace return address
- RTS
-
- ; PROCEDURE StartCounter;
- ; StartCounter sets the counter value to 1
- StartCounter
- LEA Counter,A0 ; point to the counter
- MOVE.L #1,(A0) ; set it to 1
- RTS
-
- ; FUNCTION GetCounter : LongInt;
- ; GetCounter returns a longword that is the value of the counter
- GetCounter
- MOVE.L A0,-(SP)
- LEA Counter,A0 ; point to the counter
- MOVE.L (A0),8(SP) ; return it as the function result
- MOVE.L (SP)+,A0
- RTS
-
- ; PROCEDURE QuitTimer;
- ; Call QuitTimer when your application is done or the system will crash.
- QuitTimer
- MOVEM.L A0-A1,-(SP)
- MOVE.L VIA,A1 ; Disable 6522 interrupts
- MOVE.B #$40,vIER(A1)
- LEA PrevIVC,A1 ; Restore previous interrupt vector
- MOVE.L #Lvl1dt,A0
- MOVE.L (A1),24(A0)
- MOVEM.L (SP)+,A0-A1
- RTS
-
- ; This is the interrupt handler routine for the counter. When the
- ; system gets this far A1 contains the base address of the VIA.
- ; It also preserves D0-D3/A0-A3.
- CounterIntHand
- LEA Counter,A0 ; point to the counter
- ADDQ.L #1,(A0) ; Increment it
- MOVE.B vT1C(A1),D0 ; Clear the interrupt flag on the 6522
- RTS
-
- Counter DC.L 1 ; The counter
- PrevIVC DC.L 0 ; Previous interrupt vector
-
- END
-